import React, { Component, PropTypes } from 'react';
import classNames from 'classnames';
import brace from 'brace';
import { debounce } from 'lodash';

import AceEditor from './ace-editor.jsx';

import 'brace/mode/yaml';
import 'brace/mode/text';
import 'brace/mode/typescript';
import 'brace/keybinding/vim';
import 'brace/theme/monokai';
import 'brace/ext/keybinding_menu.js';
import 'brace/ext/statusbar.js';
import 'brace/ext/searchbox.js';

const EXTNAME_PATTERN = /.*\.(.*)/;

/**
 * Resolve extname from a given file path
 * @param  {string} path
 * @return {string}
 */
function getExtname(path) {
    let matches = EXTNAME_PATTERN.exec(path);

    if (matches) {
        return matches[1];
    } else {
        return 'text';
    }
}

/**
 * 编辑器
 * @class {Editor}
 * @extends {React.Component}
 */
export default class Editor extends Component {

    static propTypes = {
        /**
         * 是否为当前选中项
         * @type {boolean}
         */
        active: PropTypes.bool,

        /**
         * 文件路径
         * @type {string}
         */
        path: PropTypes.string,

        /**
         * 读取文件
         * @type {Function}
         */
        readFile: PropTypes.func,

        /**
         * 保存文件
         * @type {Function}
         */
        saveFile: PropTypes.func,

        /**
         * 标记文件是否修改过
         * @type {Function}
         */
        markDirty: PropTypes.func,

        /**
         * 更新光标状态
         * @type {Function}
         */
        updateCursor: PropTypes.func,

        /**
         * 更新编辑器选中状态
         * @type {Function}
         */
        updateSelection: PropTypes.func,

        /**
         * 显示消息提示栏
         * @type {Function}
         */
        showNotification: PropTypes.func,
    };

    /**
     * 获取编辑器唯一 ID
     * @return {string}
     */
    getEditorName() {
        return `editor-${this.props.path}`;
    }

    /**
     * 获取 Ace Editor 实例
     * @return {AceEdtior}
     */
    getEditor() {
        const editorName = this.getEditorName();
        return brace.edit(editorName);
    }

    /**
     * 渲染逻辑
     * @param  {Object} nextProps
     * @return {bool}
     */
    shouldComponentUpdate(nextProps, nextState) {
        const { active, path } = this.props;

        return active !== nextProps.active ||
            path !== nextProps.path;
    }

    componentDidMount() {
        const { readFile, path } = this.props;
        const editor = this.getEditor();

        return readFile(path)
            .then(({ value }) => {
                // Using editor.session.setValue() to
                // reset undo manager
                editor.session.setValue(value);
            });
    }

    onLoad = (editor) => {
        const { path, markDirty, saveFile, updateSelection, updateCursor,
            showNotification } = this.props;

        const onStatusUpdate = debounce(() => {
            const selection = editor.selection;

            if (!selection.isEmpty()) {
                let range = editor.getSelectionRange();
                let text = editor.session.getTextRange(range);
                // 记录光标数量
                let selectionCount = selection.rangeCount ?
                    selection.rangeCount : 1;

                updateSelection({
                    count: selectionCount,
                    text,
                    wordCount: text.length,
                    lineCount: range.end.row - range.start.row + 1,
                    start: range.start,
                    end: range.end
                });
            } else {
                updateSelection({
                    count: 0
                });
            }

            // 更新光标位置
            updateCursor({
                row: selection.lead.row,
                column: selection.lead.column
            });
        }, 100);

        const onInput = () => {
            const isDirty = !(editor.session.getUndoManager().isClean());
            markDirty(path, isDirty);
        };

        // 自定义快捷键
        editor.commands.bindKeys({
            'Command-S': () => {
                const content = editor.getValue();

                return saveFile(path, content)
                    .then(response => {
                        // Reset as clean
                        editor.session.getUndoManager().markClean();
                        // Trigger `onInput` to update
                        onInput();
                        return showNotification({
                            text: `File "${path}" saved.`,
                            type: 'success',
                            duration: 1500
                        });
                    });
            }
        });

        editor.on('input', onInput);

        editor.on('changeStatus', onStatusUpdate);
        editor.on('changeSelection', onStatusUpdate);
        editor.on('keyboardActivity', onStatusUpdate);
    };

    render() {
        const { active, path } = this.props;
        let mode;

        switch (getExtname(path)) {
            case 'raml':
            case 'yaml':
                mode = 'yaml';
                break;
            case 'json':
            case 'js':
            case 'es':
                mode = 'typescript';
                break;
            default:
                mode = 'txt';
        }

        return (
            <div className="editor" className={classNames('editor', { active })}>
                <AceEditor name={this.getEditorName()}
                    mode={mode}
                    theme="monokai"
                    width="100%"
                    height="100%"
                    fontSize={14}
                    onLoad={this.onLoad}
                    onChange={this.onChange}
                    editorProps={{
                        $blockScrolling: Infinity
                    }} />
            </div>
        );
    }
}
